package com.bc.bcapi.service;

import com.bc.bcapi.dao.*;
import com.bc.bcapi.utils.Ognl;
import com.bc.bcapi.utils.web3j.ERC20TransactionModel;
import com.bc.bcapi.utils.web3j.Util;
import com.bc.bccore.redis.RedisService;
import com.bc.bccore.web3j.WalletService;
import com.bc.bccore.webconfig.WebConfig;
import com.bc.bccore.zbconfig.ZbConfig;
import com.bc.bcmodel.consts.MultiLanguage;
import com.bc.bcmodel.domain.*;
import com.bc.bcmodel.req.CommReq;
import com.bc.bcmodel.req.EosTokenId;
import com.bc.bcmodel.req.MngWithdrawListReq;
import com.bc.bcmodel.req.WithdrawalRequest;
import com.bc.bcmodel.resp.UserWithdrawHisResp;
import com.bc.commoncore.util.*;
import com.bc.commonentity.common.JsonResult;
import com.bc.commonentity.enums.HttpReturnRnums;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.math.BigInteger;

import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

/**
 * @Name T_User_withdrawService * @Description TODO * @Author generation * @Date 2018/8/14 15:26:25 * @Version 1.0
 **/
@Service
public class T_User_withdrawService {
    @Resource
    T_User_withdrawMapper t_User_withdrawMapper;
    @Resource
    RedisService redisService;
    @Resource
    T_Account_logMapper t_account_logMapper;
    @Resource
    T_BalanceMapper t_balanceMapper;
    @Resource
    T_Wallet_infoMapper t_wallet_infoMapper;
    @Resource
    T_OrderService t_orderService;
    @Resource
    T_Erc_contractMapper t_erc_contractMapper;
    @Resource
    T_TxhashMapper t_txhashMapper;
    @Resource
    T_SysconfService t_sysconfService;

    /**
     * 保存单条数据     *      * @return
     */

    public int insert(T_User_withdrawModel req) {
        return t_User_withdrawMapper.insert(req);
    }

    /**
     * 批量增加数据     *      * @return
     */

    public int insertBatch(List<T_User_withdrawModel> req) {
        return t_User_withdrawMapper.insertBatch(req);
    }

    /**
     * 根据对象属性查找满足条件的第一条数据     *      * @return
     */

    public T_User_withdrawModel findByNamedParam(T_User_withdrawModel req) {
        return t_User_withdrawMapper.findByNamedParam(req);
    }

    /**
     * 根据对象查询所有数据     *      * @return
     */

    public List<T_User_withdrawModel> getAllList(T_User_withdrawModel req) {
        return t_User_withdrawMapper.findByNamedParamList(req);
    }

    /**
     * 根据对象查询所有分页数据     *      * @return
     */

    public PageInfo<T_User_withdrawModel> getList(MngWithdrawListReq req) {
        PageHelper.startPage(req.getPage_index(), req.getPage_size());
        List<T_User_withdrawModel> list = t_User_withdrawMapper.getList(req);
        return new PageInfo<T_User_withdrawModel>(list);
    }

    /**
     * 更新指定对象     *      * @return
     */

    public int update(T_User_withdrawModel req) {
        return t_User_withdrawMapper.update(req);
    }

    /**
     * 删除指定数据     *      * @return
     */

    public int delete(BigInteger id) {
        return t_User_withdrawMapper.delete(id);
    }

    /**
     * 根据条件统计数据量     *      * @return
     */

    public long count(T_User_withdrawModel req) {
        return t_User_withdrawMapper.count(req);
    }

    /**
     * 审核提现逻辑，分部式锁
     */
    public JsonResult success(T_User_withdrawModel req) throws Exception {
        String key = "user_success:" + req.getWithdraw_id();
        try {
            if (!redisService.setnx(key, "1")) {
                return new JsonResult(HttpReturnRnums.WITHDRAW_AUTH);
            }
            return withdraw(req);
        } finally {
            try {
                redisService.remove(key, true);
            } catch (Exception e) {
                return new JsonResult(0, "该信息正在被锁定，不允许操作");
            }
        }
    }

    /**
     * 审核提现逻辑
     * 1. withdraw插入提现记录
     * 2. 发送transcation
     * 3. 冻结 wallet
     * 4. 记录t_txhash
     */

    @Transactional(rollbackFor = {
            Exception.class}, isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRED)
    public JsonResult withdraw(T_User_withdrawModel req) throws Exception {
        JsonResult js = new JsonResult();
        //验证审核状态
        String withdraw_id = req.getWithdraw_id();
        T_User_withdrawModel withdrawModel = t_User_withdrawMapper.getByWithdrawId(withdraw_id);
        if (withdrawModel == null) throw new Exception("not valid withdraw request");
        if (withdrawModel.getAudit_status() != 0) throw new Exception("not valid audit status withdraw request");

        withdrawModel.setAudit_status(1);
        update(withdrawModel);
        String to = withdrawModel.getOutside_address();

        if (withdrawModel.getService_token().equals("EOS")) {
            withdrawEos(withdrawModel);
        } else {
            withdrawEth(withdraw_id, withdrawModel, to);
        }
        return js;
    }

    /*提现EOS*/
    private void withdrawEos(T_User_withdrawModel req) {
        WithdrawalRequest withdrawalRequest = new WithdrawalRequest();
        withdrawalRequest.setUserId(req.getUser_id());

//        BigDecimal actual_account = new BigDecimal(t_sysconfService.getPlatformCommission() + "%").
//                multiply(req.getActual_account());
//        withdrawalRequest.setAmount(actual_account);
        EosTokenId eosTokenId = new EosTokenId();
        eosTokenId.setAccount(req.getToken_name());
        eosTokenId.setSymbol(req.getService_token());
        withdrawalRequest.setToken(eosTokenId);
        withdrawalRequest.setToAccount(req.getWithdraw_account().toString());
        withdrawalRequest.setToAccount(req.getOutside_address());
        /*这里的备注可能需要写，因为可能有用户是直接提现到交所*/
        withdrawalRequest.setMemo("");

        String param = JsonUtil.obj2string(withdrawalRequest);
        HttpUtil.sendJsonPost(ZbConfig.getEosUrl() + "eos/withdrawal", param);
    }

    /*提现ETH*/
    private void withdrawEth(String withdraw_id, T_User_withdrawModel withdrawModel, String to) throws Exception {
        String contract_id = withdrawModel.getToken_name();
        T_Erc_contractModel contractModel = t_erc_contractMapper.selectContractByContractID(contract_id);
        if (contractModel == null) throw new Exception("load contract error");

        BigInteger gasLimit = Util.getETHGASLIMIT();
        if (!contract_id.equals(Util.getETHID())) gasLimit = Util.getCONTRACTGASLIMIT();
        BigDecimal gas = withdrawModel.getGas_account();
        BigInteger gasPrice = Util.convertEth2Wei(gas).divide(gasLimit);
        BigDecimal actual_amount = withdrawModel.getActual_account();
        BigInteger actual_value = Util.Token2Wei(actual_amount, contractModel.getDigit());

        String pk = Util.getColdWalletPk();
        String txhash;
        if (contract_id.equals(Util.getETHID()))
            txhash = WalletService.getInstance().sendETHTransaction(pk, to, actual_value, gasPrice, gasLimit);
        else
            txhash = WalletService.getInstance().sendContractTransaction(pk, contractModel.getContract_address(), to, gasPrice, gasLimit, actual_value);
        if (Ognl.isBlank(txhash)) throw new Exception("sent withdraw error");

        T_TxhashModel txhashModel = new T_TxhashModel();
        txhashModel.setTxhash(txhash);
        txhashModel.setWithdraw_id(withdraw_id);
        txhashModel.setTranstype(1);
        if (t_txhashMapper.insert(txhashModel) < 0) throw new Exception("insert into withdraw txhash error");
    }

    public JsonResult withdrawHistory(HttpServletRequest request, CommReq req, String user_id) {
        PageHelper.startPage(req.getPage_index(), req.getPage_size());
        List<UserWithdrawHisResp> reps = t_User_withdrawMapper.getUserWithdrawHis(user_id);
        String languageCookie = CookieUtils.getCookie(request, "local");
        String language = "CH";
        if (!StringUtils.isEmpty(languageCookie) && MultiLanguage.acceptLanguages.containsKey(languageCookie))
            language = MultiLanguage.acceptLanguages.get(languageCookie);
        for (UserWithdrawHisResp rep : reps) rep.setLanguage(language);
        return new JsonResult(new PageInfo<>(reps));
    }

    /*提现成功
    1. withdraw更新为完成
    2. t_account_log 插入支出记录 Q
    3. t_walletinfo 释放freeze，amount - Q
    4. t_balance 插入手续费收入 Q千3
    5. t_balance 插入 支出 Q
     */
    @Transactional(rollbackFor = {
            Exception.class}, isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRED)
    public void withdrawSuccess(T_User_withdrawModel t_user_withdrawModel) throws Exception {
        t_user_withdrawModel.setPay_status(1);
        t_User_withdrawMapper.update(t_user_withdrawModel);

        String user_id = t_user_withdrawModel.getUser_id();
        String token = t_user_withdrawModel.getToken_name();
        BigDecimal account = t_user_withdrawModel.getWithdraw_account();

        T_Account_logModel withdraw_logModel = t_orderService.init_account_log(
                user_id, account, token, "提币"
        );
        if (t_account_logMapper.insert(withdraw_logModel) <= 0) throw new Exception("insert account log error");

        HashMap<String, Object> userWalletMap = new HashMap<>();
        userWalletMap.put("user_id", user_id);
        userWalletMap.put("contract_id", token);
        T_Wallet_infoModel userWallet = t_wallet_infoMapper.selectForUpdate(userWalletMap);

        HashMap<String, Object> userWalletUpdateMap = new HashMap<>();
        userWalletUpdateMap.put("id", userWallet.getId());
        userWalletUpdateMap.put("withdraw_account", account);
        t_wallet_infoMapper.withdrawSuccess(userWalletUpdateMap);

        if (!token.equals(Util.getETHID())) {
            HashMap<String, Object> ethWalletMap = new HashMap<>();
            ethWalletMap.put("user_id", user_id);
            ethWalletMap.put("contract_id", Util.getETHID());
            T_Wallet_infoModel ethWallet = t_wallet_infoMapper.selectForUpdate(ethWalletMap);

            HashMap<String, Object> ethWalletUpdateMap = new HashMap<>();
            ethWalletUpdateMap.put("id", ethWallet.getId());
            ethWalletUpdateMap.put("withdraw_account", t_user_withdrawModel.getGas_account());
            t_wallet_infoMapper.withdrawSuccess(ethWalletUpdateMap);
        }

        List<T_BalanceModel> balanceModelList = new ArrayList<>();

        T_BalanceModel commissionBalanceModel = new T_BalanceModel();
        commissionBalanceModel.setToken_type(token);
        commissionBalanceModel.setTrans_type("手续费收入");
        commissionBalanceModel.setTrans_id(t_user_withdrawModel.getWithdraw_id());
        commissionBalanceModel.setValue(t_user_withdrawModel.getService_charge());
        balanceModelList.add(commissionBalanceModel);

        T_BalanceModel withdrawBalanceModel = new T_BalanceModel();
        withdrawBalanceModel.setToken_type(token);
        withdrawBalanceModel.setTrans_type("提现");
        withdrawBalanceModel.setTrans_id(t_user_withdrawModel.getWithdraw_id());
        withdrawBalanceModel.setValue(t_user_withdrawModel.getWithdraw_account());
        balanceModelList.add(withdrawBalanceModel);

        if (!token.equals(Util.getETHID())) {
            T_BalanceModel withdrawGasBalanceModel = new T_BalanceModel();
            withdrawGasBalanceModel.setToken_type(Util.getETHID());
            withdrawGasBalanceModel.setTrans_type("提现GAS支出");
            withdrawGasBalanceModel.setTrans_id(t_user_withdrawModel.getWithdraw_id());
            withdrawGasBalanceModel.setValue(t_user_withdrawModel.getGas_account());
            balanceModelList.add(withdrawGasBalanceModel);
        }

        if (t_balanceMapper.insertBatch(balanceModelList) <= 0) throw new Exception("insert withdraw balance error");
    }

    /*提现失败
    1。withdraw更新为失败
    2. 钱包释放freeze
    */
    @Transactional(rollbackFor = {
            Exception.class}, isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRED)
    public void withdrawFailed(T_User_withdrawModel t_user_withdrawModel) {
        t_user_withdrawModel.setPay_status(0);
        t_User_withdrawMapper.update(t_user_withdrawModel);

        String user_id = t_user_withdrawModel.getUser_id();
        String token = t_user_withdrawModel.getToken_name();
        BigDecimal amount = t_user_withdrawModel.getWithdraw_account();

        updateWithdrawFail(user_id, token, amount);

        if (!token.equals(Util.getETHID()))
            updateWithdrawFail(user_id, Util.getETHID(), t_user_withdrawModel.getGas_account());
    }

    private void updateWithdrawFail(String uid, String contract_id, BigDecimal amount) {
        HashMap<String, Object> selectMap = new HashMap<>();
        selectMap.put("user_id", uid);
        selectMap.put("contract_id", contract_id);
        T_Wallet_infoModel wallet = t_wallet_infoMapper.selectForUpdate(selectMap);

        HashMap<String, Object> updateMap = new HashMap<>();
        updateMap.put("id", wallet.getId());
        updateMap.put("withdraw_account", amount);
        t_wallet_infoMapper.withdrawFail(updateMap);
    }

    /**
     * 提现审核拒绝
     */
    public JsonResult fail(T_User_withdrawModel req) {

        Integer status = req.getAudit_status();
        String key = "user_fail:" + req.getWithdraw_id();
        try {
            if (!redisService.setnx(key, "1")) {
                return new JsonResult(HttpReturnRnums.WITHDRAW_AUTH);
            }
            return withdraw_fail(req);
        } finally {
            redisService.remove(key, true);
        }
    }

    /**
     * 提现失败业务处理
     */
    @Transactional(rollbackFor = {
            Exception.class}, isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRED)
    public JsonResult withdraw_fail(T_User_withdrawModel req) {
        //        这里返回的肯定是2
//        审核状,0:待审核,1:审核通过,2:审核拒绝
        //优先更新审核状态为拒绝

        T_User_withdrawModel t_user_withdrawModel = new T_User_withdrawModel();
        t_user_withdrawModel.setWithdraw_id(req.getWithdraw_id());
        t_user_withdrawModel.setWithdraw_refuse(req.getWithdraw_refuse());
        int resultCount = t_User_withdrawMapper.updateFailStatus(t_user_withdrawModel);
        if (resultCount <= 0) {
            return new JsonResult(0, "审核状态异常");
        }

        T_User_withdrawModel withdrawModel = t_User_withdrawMapper.getByWithdrawId(req.getWithdraw_id());

        String user_id = withdrawModel.getUser_id();
        String token = withdrawModel.getToken_name();
        BigDecimal amount = withdrawModel.getWithdraw_account();

        updateWithdrawFail(user_id, token, amount);

        if (!token.equals(Util.getETHID()))
            updateWithdrawFail(user_id, Util.getETHID(), t_user_withdrawModel.getGas_account());

        //默认执行成功
        return new JsonResult();
    }

}